﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Threading;
using Lidgren.Network;
using IMLibrary.Net.Enum;

namespace IMLibrary.Server
{
    public class P2P_Server
    {
        /// <summary>
        /// 侦听端口
        /// </summary>
        /// <param name="port"></param>
        public P2P_Server(int port)
        {
            _UdpPort = port;
            IniServer();
        }
        public P2P_Server( )
        {
            IniServer();
        }
         
        private void IniServer( )
        { 
            config = new NetPeerConfiguration("masterserver");
            config.SetMessageTypeEnabled(NetIncomingMessageType.UnconnectedData, true);
            config.Port = _UdpPort;
            TurnTimer.Elapsed += (TurnTimer_Elapsed);
        }

        void TurnTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            if (isCheck) return;
            isCheck = true;
            foreach (var ts in UDPTurnServers.ToArray())
            {
                if (System.Math.Abs(DateTime.Now.Subtract(ts.LastActiveTime).Seconds) > 30)
                {
                    ts.Close();
                    UDPTurnServers.Remove(ts);
                }
            }
            isCheck = false;
        }

        #region 字段和属性
        List<UDPTurnServer> UDPTurnServers = new List<UDPTurnServer>();
        /// <summary>
        /// 中转服务空闲检测
        /// </summary>
        System.Timers.Timer TurnTimer = new System.Timers.Timer(30000);//30秒检测一次
        /// <summary>
        /// 标记是否在检测
        /// </summary>
        bool isCheck = false;
        /// <summary>
        /// 服务配置
        /// </summary>
        NetPeerConfiguration config;
        /// <summary>
        /// 服务器节点
        /// </summary>
        NetPeer peer;
        /// <summary>
        /// 线程
        /// </summary>
        private Thread thUdpServer = null;
        /// <summary>
        /// 已注册的客户端
        /// </summary>
        Dictionary<long, IPEndPoint[]> registeredHosts = new Dictionary<long, IPEndPoint[]>();
        /// <summary>
        /// UDP侦听端口
        /// </summary>
        int _UdpPort = 14343;

        /// <summary>
        /// 是否启动
        /// </summary>
        bool isStart = false;
        /// <summary>
        /// UDP服务器端口
        /// </summary>
        public int UdpPort
        { 
            get { return _UdpPort; }
        }
        /// <summary>
        /// UDP中转数
        /// </summary>
        public int TurnCount
        {
            get { return UDPTurnServers.Count; }
        }
        #endregion

        #region 服务
        /// <summary>
        /// 开始服务
        /// </summary>
        public void Start()
        { 
            TurnTimer.Start();
            isStart = true;
            thUdpServer = new Thread(StartServer);
            thUdpServer.Start( );
        }

        /// <summary>
        /// 停止服务
        /// </summary>
        public void Stop()
        {
            foreach (var ts in UDPTurnServers)
                ts.Close();  
            UDPTurnServers.Clear();

            TurnTimer.Stop();

            isStart = false;
            if (peer != null)
                peer.Shutdown("shutdown");

            if (thUdpServer != null)
                thUdpServer.Abort();
        }
        #endregion

        #region  开始服务
        private void StartServer()
        {
            peer = new NetPeer(config);
            peer.Start(); 
            while (isStart)
            {
                NetIncomingMessage msg;
                while ((msg = peer.ReadMessage()) != null)
                {
                    try
                    {
                        switch (msg.MessageType)
                        {
                            case NetIncomingMessageType.UnconnectedData:
                                if (msg.LengthBytes < 1) break;
                                // We've received a message from a client or a host
                                #region by design, the first byte always indicates action
                                switch ((MasterServerMessageType)msg.ReadByte())
                                {
                                    #region 申请中转服务
                                    case MasterServerMessageType.RequestTurn:
                                        {
                                            if (msg.LengthBytes < 9) break; 
                                            var remoteHostid = msg.ReadInt64();
                                            if (registeredHosts.ContainsKey(remoteHostid))
                                            {
                                                UDPTurnServer tsLocal = new UDPTurnServer();//创建中转套接字
                                                tsLocal.Start();
                                                UDPTurnServer tsRemote = new UDPTurnServer();//创建中转套接字
                                                tsRemote.Start();

                                                tsLocal.RemoteClient = tsRemote;
                                                tsRemote.RemoteClient = tsLocal;

                                                NetOutgoingMessage omRemote = peer.CreateMessage();
                                                omRemote.Write((byte)MasterServerMessageType.RequestTurn);
                                                omRemote.Write(tsRemote.Port);
                                                peer.SendUnconnectedMessage(omRemote, registeredHosts[remoteHostid][1]);

                                                NetOutgoingMessage omLocal = peer.CreateMessage();
                                                omLocal.Write((byte)MasterServerMessageType.RequestTurn);
                                                omLocal.Write(tsLocal.Port);

                                                peer.SendUnconnectedMessage(omLocal, msg.SenderEndPoint);

                                                UDPTurnServers.Add(tsLocal);
                                                UDPTurnServers.Add(tsRemote);
                                            }
                                        }
                                        break; 
                                    #endregion

                                    #region 注册服务
                                    case MasterServerMessageType.RegisterHost:
                                        {
                                            if (msg.LengthBytes < 16) break;

                                            // It's a host wanting to register its presence
                                            var id = msg.ReadInt64(); // server unique identifier

                                            Console.WriteLine("Got registration for host " + id);
                                            registeredHosts[id] = new IPEndPoint[]
									        {
										        msg.ReadIPEndPoint(), // internal
										        msg.SenderEndPoint // external
									        };

                                            // send registered host to client
                                            NetOutgoingMessage om = peer.CreateMessage();
                                            om.Write((byte)MasterServerMessageType.RegisterHost);
                                            om.Write(id);
                                            om.Write(registeredHosts[id][0]);
                                            om.Write(registeredHosts[id][1]);
                                            peer.SendUnconnectedMessage(om, msg.SenderEndPoint);
                                        }
                                        break;
                                    #endregion

                                    #region 请求列表服务
                                    case MasterServerMessageType.RequestHostList:
                                        {
                                            // It's a client wanting a list of registered hosts
                                            Console.WriteLine("Sending list of " + registeredHosts.Count + " hosts to client " + msg.SenderEndPoint);
                                            foreach (var kvp in registeredHosts)
                                            {
                                                // send registered host to client
                                                NetOutgoingMessage om = peer.CreateMessage();
                                                om.Write(kvp.Key);
                                                om.Write(kvp.Value[0]);
                                                om.Write(kvp.Value[1]);
                                                peer.SendUnconnectedMessage(om, msg.SenderEndPoint);
                                            }
                                        }
                                        break;
                                    #endregion

                                    #region 请求NAT穿越服务
                                    case MasterServerMessageType.RequestIntroduction:
                                        {
                                            if (msg.LengthBytes < 18) break;
                                            // It's a client wanting to connect to a specific (external) host
                                            IPEndPoint clientInternal = msg.ReadIPEndPoint();
                                            long hostId = msg.ReadInt64();
                                            string token = msg.ReadString();

                                            Console.WriteLine(msg.SenderEndPoint + " requesting introduction to " + hostId + " (token " + token + ")");

                                            // find in list
                                            IPEndPoint[] elist;
                                            if (registeredHosts.TryGetValue(hostId, out elist))
                                            {
                                                // found in list - introduce client and host to eachother
                                                Console.WriteLine("Sending introduction...");
                                                peer.Introduce(
                                                    elist[0], // host internal
                                                    elist[1], // host external
                                                    clientInternal, // client internal
                                                    msg.SenderEndPoint, // client external
                                                    token // request token
                                                );
                                            }
                                            else
                                            {
                                                Console.WriteLine("Client requested introduction to nonlisted host!");
                                            }
                                        }
                                        break;
                                    #endregion
                                }
                                break;
                                #endregion
                            case NetIncomingMessageType.DebugMessage:
                            case NetIncomingMessageType.VerboseDebugMessage:
                            case NetIncomingMessageType.WarningMessage:
                            case NetIncomingMessageType.ErrorMessage:
                                // print diagnostics message
                                Console.WriteLine(msg.ReadString());
                                break;
                        }
                    }
                    catch { }
                }
                System.Threading.Thread.Sleep(1);
            }
        }
        #endregion
    }
}
